﻿using HDF.PInvoke;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace HDF5Demo
{
    public class Program
    {
        const string FileName = "matrix_demo.h5";
        const string DatasetName = "matrix002";

        static void Main(string[] args)
        {
            int rows = 10000;
            int cols = 15;
            double[,] matrix = CreateRandomMatrix(rows, cols);
            SaveMatrixToHDF5(matrix);
            double[,] queriedData = QueryMatrix(0, 5, 0, 3);
            Console.WriteLine("查询结果（前 5 行，前 3 列）：");
            PrintMatrix(queriedData);

            UpdateMatrixRow(10, new double[cols]);
            Console.WriteLine("第 10 行已更新为全 1");

            double[] updatedRow = QueryMatrixRow(10);
            Console.WriteLine("第 10 行更新后的数据：");
            Console.WriteLine(string.Join(", ", updatedRow));
            Console.ReadLine();
        }

        static double[,] CreateRandomMatrix(int rows, int cols)
        {
            var random = new Random();
            double[,] matrix = new double[rows, cols];
            for (int i = 0; i < rows; i++)
            {
                for (int j = 0; j < cols; j++)
                {
                    matrix[i, j] = random.NextDouble();
                }
            }
            return matrix;
        }

        static void SaveMatrixToHDF5(double[,] matrix)
        {

            long fileId = H5F.create(FileName, H5F.ACC_TRUNC);

            ulong[] dims = { (ulong)matrix.GetLength(0), (ulong)matrix.GetLength(1) };

            long spaceId = H5S.create_simple(dims.Length, dims, null);

            long datasetId = H5D.create(fileId, DatasetName, H5T.NATIVE_DOUBLE, spaceId);

            GCHandle handle = GCHandle.Alloc(matrix, GCHandleType.Pinned);
            H5D.write(datasetId, H5T.NATIVE_DOUBLE, H5S.ALL, H5S.ALL, H5P.DEFAULT, handle.AddrOfPinnedObject());
            handle.Free();

            H5D.close(datasetId);
            H5S.close(spaceId);
            H5F.close(fileId);

            Console.WriteLine($"矩阵已保存到 {FileName}");
        }

        static double[,] QueryMatrix(int startRow, int rowCount, int startCol, int colCount)
        {
            long fileId = H5F.open(FileName, H5F.ACC_RDONLY);
            long datasetId = H5D.open(fileId, DatasetName);

            ulong[] offset = { (ulong)startRow, (ulong)startCol };
            ulong[] count = { (ulong)rowCount, (ulong)colCount };

            long spaceId = H5D.get_space(datasetId);
            H5S.select_hyperslab(spaceId, H5S.seloper_t.SET, offset, null, count, null);

            long memSpaceId = H5S.create_simple(2, count, null);

            double[,] buffer = new double[rowCount, colCount];
            GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
            H5D.read(datasetId, H5T.NATIVE_DOUBLE, memSpaceId, spaceId, H5P.DEFAULT, handle.AddrOfPinnedObject());
            handle.Free();

            H5S.close(memSpaceId);
            H5S.close(spaceId);
            H5D.close(datasetId);
            H5F.close(fileId);

            return buffer;
        }

        static void UpdateMatrixRow(int rowIndex, double[] newData)
        {
            long fileId = H5F.open(FileName, H5F.ACC_RDWR);
            long datasetId = H5D.open(fileId, DatasetName);

            ulong[] offset = { (ulong)rowIndex, 0 };
            ulong[] count = { 1, (ulong)newData.Length };

            long spaceId = H5D.get_space(datasetId);
            H5S.select_hyperslab(spaceId, H5S.seloper_t.SET, offset, null, count, null);

            long memSpaceId = H5S.create_simple(2, count, null);

            GCHandle handle = GCHandle.Alloc(newData, GCHandleType.Pinned);
            H5D.write(datasetId, H5T.NATIVE_DOUBLE, memSpaceId, spaceId, H5P.DEFAULT, handle.AddrOfPinnedObject());
            handle.Free();

            H5S.close(memSpaceId);
            H5S.close(spaceId);
            H5D.close(datasetId);
            H5F.close(fileId);
        }

        static double[] QueryMatrixRow(int rowIndex)
        {
            double[,] result = QueryMatrix(rowIndex, 1, 0, 15);
            double[] row = new double[result.GetLength(1)];
            for (int i = 0; i < row.Length; i++)
            {
                row[i] = result[0, i];
            }
            return row;
        }

        static void PrintMatrix(double[,] matrix)
        {
            for (int i = 0; i < matrix.GetLength(0); i++)
            {
                for (int j = 0; j < matrix.GetLength(1); j++)
                {
                    Console.Write($"{matrix[i, j]:F4} ");
                }
                Console.WriteLine();
            }
        }
    }
}
